出处:掘金
原作者:ErpanOmer
干前端的,天天都在跟 CSS 打交道。以前我们写 CSS,要是遇到复杂点的场景,比如“当一个卡片里有图片时,给卡片加个边框”,或者要给一长串不同的标题应用同一个样式,那代码写起来,说实话,挺啰嗦的
但时代变了。CSS 自己也越来越“聪明”
今天想聊的这三个选择器 —— :has() , :is() , :where(),可能有些同学还不太熟,但我敢说,一旦你开始用它们,绝对会有一种“卧槽,原来 CSS 可以这么写?”的舒爽感。它们不是什么花里胡哨的技能,而是能实实在在提升我们日常编码效率和代码可读性的“神器”
:has() - “父选择器”:has() 绝对是这三个里面最重要的选择器。我们一直想要一个能根据子元素来选择父元素的方法,现在,它来了
简单说,:has(selector) 的意思是:选择包含 selector 所匹配元素的那个元素
听起来有点绕,直接看例子
当卡片内有图片时,改变卡片样式
以前我们怎么写?
我们没法用 CSS 直接判断,通常得用 JS 去遍历 DOM,给有图片的卡片加个 class
// JS to add a class
document.querySelectorAll('.card').forEach(card => {
if (card.querySelector('img')) {
card.classList.add('card-with-image');
}
});
/* Then style the class */
.card-with-image {
border: 2px solid #61dafb;
}
这太麻烦了,样式的问题还得 JS 来帮忙
现在用 :has() 怎么写?(一行 CSS 搞定)
/* Select any .card that has an <img> inside it */
.card:has(img) {
border: 2px solid #61dafb;
padding: 10px;
}
看到了吗?就这么简单。 当 .card 元素“拥有”一个 img 子孙时,这个样式就生效了
表单校验高亮:当输入框后面紧跟着一个 .error 提示时,让输入框变红
/* Select an input that has an adjacent sibling .error */
input:has(+ .error-message) {
border-color: red;
}
布局调整:当一个容器里有超过 5 个子项时,改变布局
/* Select a .grid-container that has at least 6 children */
.grid-container:has(div:nth-child(6)) {
grid-template-columns: repeat(3, 1fr); /* 切换到三列布局 */
}
:has() 的出现,让 CSS 的逻辑表达能力提升了一个维度,很多以前必须 JS 出马的场景,现在 CSS 自己就能搞定
:is() 和 :where() - 别再写一长串重复的选择器了这两长得很像,功能也几乎一样,我们放一起说。它们的作用都是“匹配选择器列表中的任何一个”,可以极大地简化我们的代码
给文章里的所有标题(h1-h6)应用统一样式
以前我们怎么写?(又长又臭)
为了覆盖所有标题,我们得把它们一个个列出来
article h1,
article h2,
article h3,
article h4,
article h5,
article h6 {
color: #333;
margin-bottom: 1em;
}
要是想给这些标题的 hover 状态加个样式,还得再把上面这坨复制一遍……想想就头大
现在用 :is() 怎么写?(清爽多了)
/* One selector to rule them all */
article :is(h1, h2, h3, h4, h5, h6) {
color: #333;
margin-bottom: 1em;
}
/* Hover state is also super clean */
article :is(h1, h2, h3, h4, h5, h6):hover {
color: #61dafb;
}
:where()功能上,where() 和 is() 一模一样。上面那个例子,你把 is 换成 where,效果完全不变
它们唯一的区别,也是最关键的区别,在于“权重” (Specificity)
:is() 的权重,等于它括号里所有选择器中,权重最高的那个:where() 的权重,永远是 0这是什么意思?看个例子
:is(header, #main) p { color: blue; } /* #main 的权重是 100,所以这条规则权重是 101 */
:where(header, #main) p { color: red; } /* :where 权重是 0,所以这条规则权重是 1 */
p { color: green; } /* p 的权重是 1 */
<p> 会是 blue,因为 101 > 1:is 那行去掉,<p> 就会是 green,因为 :where 那条规则的权重是 1,和 p 一样,遵循“后来者居上”的原则:is():where()
p { color: ... })来覆盖掉你库里的样式,那么用 :where() 就非常合适聊了这么多,现在(2025 年)到底能不能在项目里用呢?
答案是:放心用!
截至目前,所有主流现代浏览器(Chrome, Firefox, Safari, Edge)的最新版本都已良好支持这三个选择器
除非你还需要兼容非常非常老的浏览器(比如 IE,那就算了……),否则在你的新项目中,大胆地用起来吧